summaryrefslogtreecommitdiffstats
path: root/src/pages/releases/[version].tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/pages/releases/[version].tsx')
-rw-r--r--src/pages/releases/[version].tsx100
1 files changed, 100 insertions, 0 deletions
diff --git a/src/pages/releases/[version].tsx b/src/pages/releases/[version].tsx
new file mode 100644
index 0000000..84d807a
--- /dev/null
+++ b/src/pages/releases/[version].tsx
@@ -0,0 +1,100 @@
1import { Page, PipelineStageError } from "corvos";
2
3import { strapiLoader } from "$/deps/strapi.tsx";
4import { releaseStrapiSchema } from "$/schemas/releases.ts";
5import { BaseLayout } from "$/ui/base/BaseLayout.tsx";
6import { ReleaseCard } from "$/ui/components/ReleaseCard.tsx";
7import { SiteHeader } from "$/ui/components/SiteHeader.tsx";
8import { CardList } from "$/ui/layouts/CardList.tsx";
9import { Container } from "$/ui/layouts/Container.tsx";
10import { HList } from "$/ui/layouts/HList.tsx";
11import { ActionButton } from "$/ui/objects/ActionButton.tsx";
12import { Badge } from "$/ui/objects/Badge.tsx";
13import { Body } from "$/ui/objects/Body.tsx";
14import { Divider } from "$/ui/objects/Divider.tsx";
15import { Heading } from "$/ui/objects/Heading.tsx";
16import { Icon } from "$/ui/objects/Icon.tsx";
17import site from "@/corvos.config.ts";
18
19export const pages = site.$.pageGenerator(async function* () {
20 for await (const item of strapiLoader.loadCollection("corvos-releases", releaseStrapiSchema, {
21 "populate[0]": "files",
22 "sort[0]": "date:desc",
23 })) {
24 if (item instanceof PipelineStageError) {
25 yield item;
26 } else {
27 yield new Page(".strapi", item.notes ?? [], {
28 ...item,
29 title: `Corvos ${item.version}`,
30 type: "release" as const,
31 });
32 }
33 }
34});
35
36export const template = site.$.template({ type: "release" }, (page, pages) => {
37 const query = site.$.query(pages);
38
39 const feed = query.type(".rss").match({ type: "releases" }).one();
40
41 const prev = query.type(".html").match({ type: "release" }).prev(page);
42 const next = query.type(".html").match({ type: "release" }).next(page);
43
44 return (
45 <BaseLayout page={page}>
46 <SiteHeader
47 mode="compact"
48 url={page.url}
49 end={
50 <div class="l-media__block">
51 <ActionButton
52 align-block
53 class="u-mbs-0"
54 href={feed.url}
55 icon={<Icon icon="rss-simple" variant="bold" />}
56 level="quiet"
57 pill
58 title="RSS Feed"
59 />
60 </div>
61 }
62 >
63 <div class="l-media__block l-media__block--main">
64 <Heading class="u-mbs-0 u-elp">{page.data.title}</Heading>
65 </div>
66 </SiteHeader>
67
68 <Container as="main" class="u-mbs-neutralize h-entry" fixed="150" pad-block pad-inline>
69 <HList aria-label="Categories">
70 <li>
71 <Badge as="strong" pill selected>
72 <time class="dt-published" datetime={page.data.date.toISOString()}>
73 {page.data.date.toLocaleDateString(undefined, {
74 day: "numeric",
75 month: "short",
76 year: "numeric",
77 })}
78 </time>
79 </Badge>
80 </li>
81 </HList>
82
83 <Heading as="h1" class="u-mbs-200 p-name" display highlight level="xxl">
84 {page.data.title}
85 </Heading>
86
87 <Body class="e-content">{{ __html: page.content }}</Body>
88 </Container>
89
90 <Divider level="faint" />
91
92 <Container as="footer" class="u-mbs-neutralize" fixed="150" pad-block pad-inline>
93 <CardList layout="grid">
94 {prev && <ReleaseCard heading-level="h2" page={prev} list />}
95 {next && <ReleaseCard heading-level="h2" page={next} list />}
96 </CardList>
97 </Container>
98 </BaseLayout>
99 );
100});